<?php

include_once dirname(dirname(__FILE__)) . '/objects/fobject.php';

class Collection extends fobject
{
	// --- single object --- {{{
	//
	// операции над (одним-единственным) элементом коллекции
	//
	// добавить если небыло
	public function add ($o)
	{
		$store = $this->store;
		$props = $this->cast_from($o);
		$oid = isset($props['oid']) ? $props['oid'] : $o->id;
		unset($props['oid']);
		$meta = $this->getMeta();
		$class_name = isset($meta['db']['name']) ? $meta['db']['name'] : $this->getTypeName();
		debug(__METHOD__."() class_name=$class_name" );

		//$store->delProperty($oid, $class_name);
		$store->addProperty($oid, $class_name, $props);
		//var_dump($class_name,$props);
		return $props;
	} // end of function add

	// если было, обновить, иначе добавить
	public function put ($o)
	{
	} // end of function put

	// если было обновить, иначе не трогать
	public function sync ($o)
	{
		$store = $this->store;
		$props = $this->cast_from($o);
		$oid = isset($props['oid']) ? $props['oid'] : $o->id;
		unset($props['oid']);
		$meta = $this->getMeta();
		$class_name = isset($meta['db']['name']) ? $meta['db']['name'] : $this->getTypeName();
		debug(__METHOD__."() class_name=$class_name" );

		$store->db->begin();
		$store->delProperty($oid, $class_name);
		$store->addProperty($oid, $class_name, $props);
		$store->db->end();
		return $props;
	} // end of function update

	// удалить (если есть)
	public function del ($o)
	{
		$store = $this->store;
		$props = $this->cast_from($o);
		$oid = isset($props['oid']) ? $props['oid'] : $o->id;
		$meta = $this->getMeta();
		$class_name = isset($meta['db']['name']) ? $meta['db']['name'] : $this->getTypeName();

		$store->db->begin();
		debug(__METHOD__."() class_name=$class_name" );
		$store->delProperty($oid, $class_name);
		$store->db->end();
		return $props;
	} // end of function delete

	// вернуть объект?, если он один такой в коллекции
	//
	// FIXME : 2007-12-04 : lucky
	// не понятно что возвращать: объект или набор свойств объекта из коллекции?
	// сейчас возвращает набор свойств,
	// хотя возможно зависит от запроса.. %)
	public function get ($ql, $params=NULL)
	{
		$res = NULL;
		//FIXME : 2007-12-04 : lucky
		// можно уменьшить размер передаваемых данных, если воткнуть в запрос "LIMIT 2" 
		$this->select($ql, $params);
		$row = $this->next();
		$other_row = $this->next();
		if ($row) { 
			if ($other_row) {
				$res = false;
			} else {
				$res = $row;
			}
		}
		return $res;
	} // end of method get
	//
	// --- single object --- }}}


	// --- set of objects --- {{{
	//
	// операции над группами элементов коллекции
	//
	public function insert ($props)
	{
	} // end of function select
	public function replace ($props)
	{
		$store = $this->store;
		$meta = $this->getMeta();
		$class_name = isset($meta['db']['name']) ? $meta['db']['name'] : $this->getTypeName();
		debug(__METHOD__."() class_name=$class_name" );

		$oid = $props['oid'];
		unset($props['oid']);
		$store->delProperty($oid, $class_name, $props);
		$store->addProperty($oid, $class_name, $props);
	} // end of function select
	public function update ($props)
	{
	} // end of function select
	public function select ($ql=NULL, $params=NULL)
	{
		$this->result = NULL;
		if (!isset($ql)) $ql = $this->ql;
		if (!isset($ql)) {
			$meta = $this->getMeta();
			$ql   = "select objects where {$meta['db']['name']}.oid > 0";
		}
		$sql = $this->getSelectSQL($ql, $params);
		if ($sql && ($dbrs = $this->store->query($sql))) {
			$this->rs = new DBStoreResult($this->store, $dbrs);
			$this->rs->method = __METHOD__;
			$this->rs->sql = $sql;
		} else {
			error (__METHOD__."($ql) error");
		}
		return $this;
	} // end of function select
	public function selectSQL ($sql)
	{
		$this->result = NULL;
		if ($sql && ($dbrs = $this->store->query($sql))) {
			$this->rs = new DBStoreResult($this->store, $dbrs);
			$this->rs->method = __METHOD__;
			$this->rs->sql = $sql;
		} else {
			error (__METHOD__."($ql) error");
		}
		return $this;
	} // end of function select
	public function delete ($props)
	{
		$store = $this->store;
		$meta = $this->getMeta();
		$class_name = isset($meta['db']['name']) ? $meta['db']['name'] : $this->getTypeName();
		debug(__METHOD__."() class_name=$class_name" );

		$oid = $props['oid'];
		unset($props['oid']);
		$store->delProperty($oid, $class_name, $props);
	} // end of function select

	public function cast_from_object ($o)
	{
		$props = array();
		$data = $o->data;
		foreach ($this->getFields() as $ft) {
			$prop = $ft->meta;
			$prop_name = isset($prop['db']['name']) ? $prop['db']['name'] : $prop['name'];
			if (array_key_exists($prop_name, $data)) {
				$value = $data[$prop_name]; 
				$props[$prop_name] = $value;
			} elseif (array_key_exists('default', $prop)) {
				$value = $prop['default'];
				$props[$prop_name] = $value;
			} else {
				$value = NULL;
				//debug(__METHOD__."() property '{$prop_name}' value not found");
			}
		}
		return $props;
	} // end of function cast_object

	// вернуть следующий элемент из текущей выборки (select())
	public function next ()
	{
		if ($this->rs) {
			return $this->rs->next();
		}
		return false;
	} // end of function next
	// для каждого элемента в текущей выборке (select()) выполнить функцию
	// первым параметром будет объект
	// можно передать дополнительные параметры
	public function each ($callback /*, $arg, $arg1, ... */)
	{
		$args = func_get_args();
		/* $args[0] contains callback, but we will replace it with object. just speedup hack. */
		while (($args[0] = $this->next())) {
			call_user_func_array($callback, $args);
		}
	} // end of function each
	//
	// --- set of objects --- }}}
	//

	public function getSelectSQL ($ql, $params=NULL)
	{
		$compiler = $this->store->qlc;
		$this->selectQueryTree = $compiler->parse($ql);

		$ctx = new stdClass();
		$ctx->params = $params;
		$this->selectSQL = $compiler->compile($this->selectQueryTree, $ctx);
		return $this->selectSQL;
	} // end of method getSelectSQL

}
